package com.iflytek.uoamp.agent.monitor.sender.loadbalance;

import com.iflytek.uoamp.agent.monitor.sender.config.Config;
import com.iflytek.uoamp.agent.monitor.sender.config.HostInfo;
import com.iflytek.uoamp.agent.monitor.sender.interfaces.ISender;
import com.iflytek.uoamp.agent.monitor.sender.interfaces.ISenderFactory;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.log4j.Logger;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;

public class ConnectionPoolManager {
	private static final int STEPLEN = 5 * 1000;
	private Queue<ISender> availableClients;
	private Set<ISender> checkedOutClients;
	private final int maxPoolSize;
	private final HostInfo host;
	private final byte[] lock;
	private final Config config;
	private ISenderFactory<ISender> factory;

	public ConnectionPoolManager(ISenderFactory<ISender> factory,Config config, HostInfo host) {
		this.factory = factory;
		this.config = config;
		this.host = host;
		this.maxPoolSize = config.getConnectPoolSize();
		this.lock = new byte[0];
		availableClients = new LinkedList<ISender>();
		checkedOutClients = new HashSet<ISender>();
	}

	public ISender checkout() throws Exception {
		ISender ret = null;
		
		int checkStep = config.getRequestTimeOut() / STEPLEN;
		int index = 0;
		try {
			synchronized (lock) {
				while(index < checkStep) {
					if (!availableClients.isEmpty()) {
						ret = availableClients.poll();
						checkedOutClients.add(ret);
					} else {
						int totalCount = availableClients.size() + checkedOutClients.size();
						if (totalCount < this.maxPoolSize) {
							ret = factory.createSender(host.getHostName(),host.getPortNumber(),config);
							if (null != ret) {
								checkedOutClients.add(ret);
							}
						}
					}
					if (null == ret) {
						lock.wait(STEPLEN);
						index++;
						Logger.getRootLogger().info("waiting for checkout client." + index);
					} else {
						break;
					}
				}
				Logger.getRootLogger().info("availableClients size = " + availableClients.size() + " checkedOutClients size = " + checkedOutClients.size());
			}
		} catch (Exception e) {
			Logger.getRootLogger().error(ExceptionUtils.getStackTrace(e),e);
		}
		return ret;
	}
	
	public void checkIn(ISender client) {
		if (null == client ) {
			return;
		}
		boolean status = client.isOpen();
		// close client
		if (!status) {
            checkedOutClients.remove(client);
			Logger.getRootLogger().info("ConnectionPoolManager is about to close the opend cliet.");
//			client.close();
		}
		
		try {
			synchronized (lock) {
				checkedOutClients.remove(client);
				if (status) {
					availableClients.add(client);
				}
				Logger.getRootLogger().info("availableClients size = " + availableClients.size() + " checkedOutClients size = " + checkedOutClients.size());
				lock.notifyAll();
			}
		} catch (Exception e) {
			Logger.getRootLogger().error(ExceptionUtils.getStackTrace(e),e);
		}
	}
	

	/*public void destroy(ISender client) {
		poolLock.lock();
		try {
			if (checkedOutClients.contains(client)) {
				checkedOutClients.remove(client);
				client.close();
			} else {
				client.close();
			}
			
			checkedOutClients.remove(client);
			currentPoolSize--;
		} finally {
			poolLock.unlock();
		}
		client.close();
	}*/

	public void closeAll() {
		try {
			synchronized (lock) {
				for (ISender c : availableClients) {
					if (c != null) {
						c.close();
					}
				}
				for (ISender c : checkedOutClients) {
					if (c != null) {
						c.close();
					}
				}
				availableClients.clear();
				checkedOutClients.clear();
			}
		} catch (Exception e) {
			Logger.getRootLogger().error(ExceptionUtils.getStackTrace(e),e);
		}
	}

}
